import { State } from '../enum'
import { Coordinate } from '../types'
import { Map } from './Map'
import { Config } from './Config'
import { Food } from './Food'
import { ControllerKeyCode } from '../../layout/enums'

export class Snake{
    direction: ControllerKeyCode
    nextDirection: ControllerKeyCode
    head: Coordinate
    tail: Coordinate

    private map: Map
    constructor(map: Map){
        this.map = map
        this.direction = ControllerKeyCode.KeyD
        this.nextDirection = this.direction

        this.head = { x: 2, y: 4 } // init snake
        let body = { x: 2, y: 3 }
        this.tail = { x: 2, y: 2}
        this.map.array[this.head.x][this.head.y] = { state: State.Head, code: 3 }
        this.map.array[body.x][body.y] = { state: State.Body, code: 2 }
        this.map.array[this.tail.x][this.tail.y] = { state: State.Tail, code: 1 }
    }
    isAlive(config: Config): boolean{
        let { x, y } = this.head
        switch(this.nextDirection){
            case ControllerKeyCode.KeyW:
                if(x - 1 < 0) return false
                else if(this.map.array[x - 1][y].state === State.Body) return false
                break
            case ControllerKeyCode.KeyD:
                if(y + 1 >= config.cols) return false
                else if(this.map.array[x][y + 1].state === State.Body) return false
                break
            case ControllerKeyCode.KeyS:
                if(x + 1 >= config.rows) return false
                else if(this.map.array[x + 1][y].state === State.Body) return false
                break
            case ControllerKeyCode.KeyA:
                if(y - 1 < 0) return false
                else if(this.map.array[x][y - 1].state === State.Body) return false
                break
            default: 
                alert('isAlive wrong!!!!!')
                break
        }
        return true
    }
    mobileRule(config: Config, food: Food){
        this.moveHead()
        if(this.whetherEatFood(food)){
            food.generateFood()
        }else{
            this.moveTail(config)
        }
    }
    private whetherEatFood(food: Food){
        return JSON.stringify(this.head) === JSON.stringify(food.coordinate)
    }
    private moveHead(){
        let { x, y } = this.head // head coordinate
        let headCode = this.map.array[x][y].code // current head code
        this.map.array[x][y].state = State.Body // head to body
        switch(this.nextDirection){
            case ControllerKeyCode.KeyW:
                x--
                break
            case ControllerKeyCode.KeyD:
                y++
                break
            case ControllerKeyCode.KeyS:
                x++
                break
            case ControllerKeyCode.KeyA:
                y--
                break
            default:
                alert('move head wrong!!!')
                break
        }
        this.head = { x, y }
        this.map.array[this.head.x][this.head.y] = { state: State.Head, code: ++headCode } //generate head
        this.direction = this.nextDirection
    }
    private moveTail(config: Config){
        let { x, y } = this.tail
        if(this.map.array[x][y].state !== State.Head) this.map.array[x][y] = { state: State.Empty, code: Infinity } // destroy tail
        // get tail up down left right point, use min code for tail
        let top: number = (x - 1 >= 0) && this.map.array[x - 1][y].state !== State.Empty ? this.map.array[x - 1][y].code : Infinity
        let right: number = (y + 1 < config.cols) && this.map.array[x][y + 1].state !== State.Empty ? this.map.array[x][y + 1].code : Infinity
        let bottom: number = (x + 1 < config.rows) && this.map.array[x + 1][y].state !== State.Empty ? this.map.array[x + 1][y].code : Infinity
        let left: number = (y - 1 >= 0) && this.map.array[x][y - 1].state !== State.Empty ? this.map.array[x][y - 1].code : Infinity
        let min = Math.min(top, right, bottom, left)
        switch(min){
            case top:
                x--
                break
            case right:
                y++
                break
            case bottom:
                x++
                break
            case left:
                y--
                break
            default:
                alert('move tail wrong!!!')
                break
        }
        this.tail = { x, y }
        this.map.array[this.tail.x][this.tail.y].state = State.Tail // generate tail
    }
    control(nextDirection: ControllerKeyCode){
        if(!this.checkDirectionLegal(nextDirection)) return
        this.nextDirection = nextDirection
    }
    private checkDirectionLegal(nextDirection: ControllerKeyCode): boolean{
        if(this.direction === ControllerKeyCode.KeyW && nextDirection === ControllerKeyCode.KeyS) return false
        else if(this.direction === ControllerKeyCode.KeyD && nextDirection === ControllerKeyCode.KeyA) return false
        else if(this.direction === ControllerKeyCode.KeyS && nextDirection === ControllerKeyCode.KeyW) return false
        else if(this.direction === ControllerKeyCode.KeyA && nextDirection === ControllerKeyCode.KeyD) return false
        else return true
    }
}